/*
 * MWT - Micro Window Toolkit
 * Copyright (C) 2007 Lucas Domanico - lucazd@gmail.com
 * 
 * Licensed under the terms of the GNU Lesser General Public License:
 * 		http://www.opensource.org/licenses/lgpl-license.php
 * 
 * For further information visit:
 * 		http://j2me-mwt.sourceforge.net/
 */

package view.font;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

/**
 * <p>The font class represents fonts, which are used to render text in a visible way.</p>
 * 
 * <p>MWT allows to use custom bitmap fonts or system fonts, depending on how the font is created.</p>
 * 
 * <br>
 * <br>
 * 
 * <h3>System Native Fonts</h3>
 * <p>System native fonts are created using {@link #Font(int, int, int, int)}.</br>
 * You can change the size, color, face and style using the appropriate values.</p>
 * <p>The main drawback of system fonts is that they may not fit with the look and feel of your
 * application as they are device dependent.</p>
 * <p>A system native font uses internally {@link javax.microedition.lcdui.Font}.</p>
 * 
 * <br>
 * <br>
 * 
 * <h3>Custom Bitmap Fonts</h3>
 * <p>Custom bitmap fonts are created using {@link #Font(Image, char[], int[], int)}
 * or {@link #Font(Image[], char[], int)}.<br>
 * </p>
 * A custom bitmap font is an immutable object, that means, you can't change the size or color.<br>
 * 
 * Please check the <a href="http://j2me-mwt.sourceforge.net/microfontmaker.html">
 * MicroFont-Maker</code>command line tool.<br> 
 *
<table width="800" border="0" cellspacing="4" cellpadding="0" style="border: 1px black solid">
  <tr>
    <td width="45">&nbsp;</td>
    <td colspan="3" bgcolor="#DBDBDB"><div align="center">System Native Fonts </div></td>
    <td colspan="3" bgcolor="#DBDBDB"><div align="center">Custom Bitmap Fonts </div></td>
  </tr>
  <tr>
    <td rowspan="3" bgcolor="#DBDBDB"><div align="center">Size</div></td>
    <td width="73" rowspan="3" valign="top" bgcolor="#CEDBFF"><p>Accepts constants values</p>    </td>
    <td width="232" bgcolor="#CEDBFF">{@link #SIZE_MEDIUM}</td>
    <td width="90" bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_size_med.png" width="90" height="30" /></td>
    <td width="60" rowspan="3" valign="top" bgcolor="#CFFFCA">Any value</td>
    <td width="225" bgcolor="#CFFFCA">17 (pixels)</td>
    <td width="43" bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_size_17.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link #SIZE_SMALL}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_size_small.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">15 (pixels)</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_size_15.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link #SIZE_LARGE}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_size_large.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">19 (pixels)</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_size_19.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td rowspan="4" bgcolor="#EEEEEE"><div align="center">Styles</div></td>
    <td rowspan="4" valign="top" bgcolor="#EAEFFF">Accepts constants values</td>
    <td bgcolor="#EAEFFF">{@link #STYLE_PLAIN}</td>
    <td bgcolor="#EAEFFF"><img src="{@docRoot}/resources/font_sys_style_plain.png" width="90" height="18" /></td>
    <td colspan="3" rowspan="4" valign="top" bgcolor="#ECFFEA">Not available (you can use different bitmap fonts for each style)</td>
  </tr>
  <tr>
    <td bgcolor="#EAEFFF">{@link #STYLE_BOLD}</td>
    <td bgcolor="#EAEFFF"><img src="{@docRoot}/resources/font_sys_style_bold.png" width="90" height="18" /></td>
  </tr>
  <tr>
    <td bgcolor="#EAEFFF">{@link #STYLE_ITALIC}</td>
    <td bgcolor="#EAEFFF"><img src="{@docRoot}/resources/font_sys_style_italic.png" width="90" height="18" /></td>
  </tr>
  <tr>
    <td bgcolor="#EAEFFF">{@link #STYLE_UNDERLINED}</td>
    <td bgcolor="#EAEFFF"><img src="{@docRoot}/resources/font_sys_style_underlined.png" width="90" height="18" /></td>
  </tr>
  <tr>
    <td rowspan="3" bgcolor="#DBDBDB"><div align="center">Face</div></td>
    <td rowspan="3" valign="top" bgcolor="#CEDBFF"><p>Accepts constants values</p>    </td>
    <td bgcolor="#CEDBFF">{@link #FACE_SYSTEM}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_style_plain.png" width="90" height="18" /></td>
    <td colspan="3" rowspan="3" valign="top" bgcolor="#CFFFCA">Not available, however, you don't need this when using custom fonts </td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link #FACE_MONOSPACE}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_face_monospace.png" width="90" height="18" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link #FACE_PROPORTIONAL}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_style_plain.png" width="90" height="18" /></td>
  </tr>
  <tr>
    <td bgcolor="#EEEEEE"><div align="center">Color</div></td>
    <td bgcolor="#EAEFFF">Any </td>
    <td bgcolor="#EAEFFF">0xA1C632</td>
    <td bgcolor="#EAEFFF"><img src="{@docRoot}/resources/font_sys_color_0xA1C632.png" width="90" height="18" /></td>
    <td bgcolor="#ECFFEA">Any </td>
    <td bgcolor="#ECFFEA">0xA1C632</td>
    <td bgcolor="#ECFFEA"><img src="{@docRoot}/resources/font_bmp_color_0xA1C632.png" width="90" height="18" /></td>
  </tr>
  <tr>
    <td rowspan="9" bgcolor="#DBDBDB"><div align="center">Anchor</div></td>
    <td rowspan="9" valign="top" bgcolor="#CEDBFF">Accepts constants values</td>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_TOP_LEFT}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_top_left.png" width="90" height="30" /></td>
    <td rowspan="9" valign="top" bgcolor="#CFFFCA">Accepts constants values</td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_TOP_LEFT}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_top_left.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_TOP_CENTER}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_top_center.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_TOP_CENTER}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_top_center.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_TOP_RIGHT}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_top_right.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_TOP_RIGHT}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_top_right.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_MIDDLE_LEFT}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_middle_left.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_MIDDLE_LEFT}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_middle_left.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_MIDDLE_CENTER}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_middle_center.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_MIDDLE_CENTER}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_middle_center.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_MIDDLE_RIGHT}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_middle_right.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_MIDDLE_RIGHT}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_middle_right.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_BOTTOM_LEFT}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_bottom_left.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_BOTTOM_LEFT}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_bottom_left.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_BOTTOM_CENTER}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_bottom_center.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_BOTTOM_CENTER}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_bottom_center.png" width="90" height="30" /></td>
  </tr>
  <tr>
    <td bgcolor="#CEDBFF">{@link Component#ALIGN_BOTTOM_RIGHT}</td>
    <td bgcolor="#CEDBFF"><img src="{@docRoot}/resources/font_sys_anchor_bottom_right.png" width="90" height="30" /></td>
    <td bgcolor="#CFFFCA">{@link Component#ALIGN_BOTTOM_RIGHT}</td>
    <td bgcolor="#CFFFCA"><img src="{@docRoot}/resources/font_bmp_anchor_bottom_right.png" width="90" height="30" /></td>
  </tr>
</table>
 * 
 * 
 * 
 * <h3><a name="extending">Extending</a></h3> 
 * <p>Classes that extend a Font must override the following methods:
 * <ul>
 * <li>{@link #clone()}</li>
 * <li>{@link #copy(Font)}</li>
 * <li>{@link #getHeight()}</li>
 * <li>{@link #getWidth(String)}</li>
 * <li>{@link #write(Graphics, String, int, int, int)}</li>
 * </ul>
 * The contructor must call the system native font {@link #Font(InputStream, char[], int, int, int)}.
 * </p>
 * Example:
 * <pre>
 * class MyFont extends Font {
 *   int newAttribute = ...;
 *   
 *   public MyFont(int newAttribute) {
 *     super(0,0,0,0);
 *     this.newAttribute = newAttribute;
 *   }
 *   
 *   protected void copy(MyFont f) {
 *     super.copy(f);
 *     f.newAttribute = this.newAttribute;
 *   }
 *   
 *   private MyFont() {}
 *   public Font clone() {
 *     MyFont f = new MyFont();
 *     copy(f);
 *     return f;
 *   }
 *   
 *   public int getHeight() {
 *     // new implementation
 *   }
 *   
 *   public int getWidth(String s) {
 *     // new implementation
 *   }
 *   
 *   protected void write(Graphics g, String text, int x, int y, int anchor) {
 *     // new implementation
 *   }
 * }
 * </pre>
 */
public class Font {
	/** The plain style constant. */
    static final public int STYLE_PLAIN = 0;
    /** The bold style constant. */
    static final public int STYLE_BOLD = 1;
    /** The italicized style constant. */
    static final public int STYLE_ITALIC = 2;
    /** The underlined style constant. */
    static final public int STYLE_UNDERLINED = 4;
    /** The "small" system-dependent font size. */
    static final public int SIZE_SMALL = 8;
    /** The "medium" system-dependent font size. */
    static final public int SIZE_MEDIUM = 0;
    /** The "large" system-dependent font size. */
    static final public int SIZE_LARGE = 16;
    /** The "system" font face. */
    static final public int FACE_SYSTEM = 0;
    /** The "monospace" font face. */
    static final public int FACE_MONOSPACE = 32;
    /** The "proportional" font face. */
    static final public int FACE_PROPORTIONAL = 64;
    
    static final public int TYPE_SYSTEM = 0;
    static final public int TYPE_MAPPED = 1;
    static final public int TYPE_STRIP = 2;
    
    private int type;
	private int color;
	private int face;
	private int size;
	private int style;
	private int charSpacing;	// the space between chars
	private int spaceWidth;		// the space char ' ' width
	private char[] charset;
	private Image[] images;
	
	private Image strip;	// since 1.2
	private int[] widths;	// since 1.2
	private int[] offsets;	// since 1.2
		
	
	/** Creates a system native font. */
	public Font(int color, int face, int size, int style) {
		this.color = color;
		this.face = face;
		this.size = size;
		this.style = style;
		this.type = TYPE_SYSTEM;
	}
	
	/**
	 * Creates a bitmap font using an {@link InputStream} formatted using
	 * <code>MicroFont-Maker</code> tool.<br>
	 * Optionally, you can specify which chars must be loaded.<br>
	 * <b>NOTE:</b> this method uses {@link Image#createImage(javax.microedition.lcdui.Image)}
	 * implementation which in some devices causes problems.
	 * Un such case, use {@link #Font(Image[], char[], int)} instead.<br>
	 * @param is the {@link InputStream} formatted using MicroFont-Maker tool
	 * @param charset The charset specifying which chars must be loaded or null to load the entire charset
	 * @param charSpacing The space within chars (negative values are valid)
	 * @param size The size value or 0 to use default size
	 * @param color The color value
	 * @throws IOException might be thrown by the {@link InputStream}
	 * @deprecated
	 */
	public Font(InputStream is, char[] charset, int charSpacing, int size, int color) throws IOException {
		this(is,charset,size,color); // micro font
		this.charSpacing = charSpacing;
		this.type = TYPE_MAPPED;
	}
	
	
	/**
	 * Creates a font giving the char images and specifying the charset.
	 * @param images
	 * @param charset The charset
	 * @param charSpacing The space within chars (negative values are valid)
	 * @since 1.1
	 */
	public Font(Image[] images, char[] charset, int charSpacing) {
		this.charSpacing = charSpacing;
		this.charset = new char[charset.length];
		System.arraycopy(charset,0,this.charset,0,charset.length);
		this.images = new Image[images.length];
		System.arraycopy(images,0,this.images,0,images.length);
		
		for(int i=0; i<images.length ;i++)
			if(images[i].getHeight()>this.size)
				this.size = images[i].getHeight();
		this.spaceWidth = this.size/2;
		
		// sort the charset (a binary search will be used later)
		for(int i=0; i<this.charset.length ;i++)
			for(int j=i+1; j<this.charset.length ;j++)
				if(this.charset[j] < this.charset[i]) {
					final char tc = this.charset[i];
					this.charset[i] = this.charset[j];
					this.charset[j] = tc;
					final Image ti = this.images[i];
					this.images[i] = this.images[j];
					this.images[j] = ti;
				}
		this.type = TYPE_MAPPED;
	}
	
	
	
	
	
//	public Font(InputStream is, int charSpacing) throws IOException {
//		DataInputStream dis = new DataInputStream(is);
//		int imgdataLength = dis.readInt();
//		byte[] imgdata = new byte[imgdataLength];
//		dis.read(imgdata);
//		Image image = Image.createImage(imgdata, 0, imgdata.length);
//		imgdata = null;
//		int charsetLength = dis.readInt();
//		char[] charset = new char[charsetLength];
//		int[] widths = new int[charsetLength];
//		for(int i=0; i<charsetLength ;i++) {
//			charset[i] = dis.readChar();
//			widths[i] = dis.readInt();
//		}
//		dis.close();
//		_Font(image, widths, charset, charSpacing);
//	}
	
	/**
	 * Creates a font-strip.
	 * @param img
	 * @param widths array of each char width
	 * @param charset
	 * @param charSpacing
	 * @since 1.2
	 */
	public Font(Image img, char[] charset, int[] widths, int charSpacing) {
		_Font(img,widths,charset,charSpacing);
	}
	final private void _Font(Image img, int[] widths, char[] charset, int charSpacing) {
		this.strip = img;
		this.size = img.getHeight();
		this.widths = new int[widths.length];
		for(int i=0; i<widths.length ;i++) this.widths[i] = widths[i];
		this.charset = new char[charset.length];
		for(int i=0; i<charset.length ;i++) this.charset[i] = charset[i];
		this.spaceWidth = this.size/2;
		this.charSpacing = charSpacing;
		this.offsets = new int[widths.length];
		for(int i=1; i<widths.length ;i++) {
			this.offsets[i] = this.offsets[i-1] + widths[i-1]; 
		}
		this.type = TYPE_STRIP;
		
		// sort the charset (a binary search will be used later)
		for(int i=0; i<this.charset.length ;i++)
			for(int j=i+1; j<this.charset.length ;j++)
				if(this.charset[j] < this.charset[i]) {
					final char tc = this.charset[i];
					this.charset[i] = this.charset[j];
					this.charset[j] = tc;
					
					final int ti = this.widths[i];
					this.widths[i] = this.widths[j];
					this.widths[j] = ti;
					
					final int to = this.offsets[i];
					this.offsets[i] = this.offsets[j];
					this.offsets[j] = to;					
				}
	}
	
	
	/**
	 * Gets a clone of this font.
	 * @see <a href="#extending">Extending a Font</a>
	 */
	public Font clone() {
		Font f = new Font(0,0,0,0);
		copy(f);
		return f;
	}
	
	/**
	 * Copies this font into the given font.
	 * @see <a href="#extending">Extending a Font</a>
	 */
	protected void copy(Font font) {
		font.color = this.color;
		font.face = this.face;
		font.size = this.size;
		font.style = this.style;
		
		font.images = this.images;
		font.charset = this.charset;
		font.charSpacing = this.charSpacing;
		font.spaceWidth = this.spaceWidth;
		font.strip = this.strip;
		font.widths = this.widths;
		font.offsets = this.offsets;
		
		font.type = this.type;
	}


	/** Checks if this font is a bitmap font. */
	final public boolean isBitmapFont() { return type != TYPE_SYSTEM; }

	/** Gets the font type: {@link #TYPE_MAPPED}, {@link #TYPE_STRIP} or {@link #TYPE_SYSTEM}  */
	final public int getType() { return type; }
	
	/** Gets the color of the font. */
	final public int getColor() { return color; }
	/** Sets the color of the font if it's not a bitmap font */
	final public void setColor(int color) { if(!isBitmapFont()) this.color = color; }
	
	/** Gets the face of the font. */
	final public int getFace() { return face; }
	/** Sets the face of the font. */
	final public void setFace(int face) { this.face = face; }
	
	/** Gets the size of the font. */
	final public int getSize() { return size; }
	/** Sets the size of the font if it's not a bitmap font */
	final public void setSize(int size) { if(!isBitmapFont()) this.size = size; }
	
	/** Gets the style of the font. */
	final public int getStyle() { return style; }
	/** Sets the style of the font. */
	final public void setStyle(int style) { this.style = style; }

	/**
	 * Gets the charset length or -1 if it is not available.<br>
	 * The default implementation, returns -1 if this is a system native font,
	 * otherwise this returns the charset length. 
	 */
	public int getCharsetLength() {
		return isBitmapFont()? charset.length : -1;
	}
	
	/**
	 * Copies the charset into the given buffer at the current offset.<br>
	 * If {@link #getCharsetLength()} returns -1, this does nothing.
	 */
	public void getCharset(char[] buffer, int offset, int length) {
		if(!isBitmapFont()) return;
		System.arraycopy(charset,0,buffer,offset,length);
	}
	
	/**
	 * Only for {@link #TYPE_MAPPED} fonts, otherwise throws a {@link RuntimeException}
	 * Copies the images into the given image array at the current offset.<br>
	 * The images length is the same as {@link #getCharsetLength()}
	 * @param images
	 * @param offset
	 * @param length
	 * @since 1.2
	 */
	public void getImages(Image[] images, int offset, int length) {
		if(!isBitmapFont()) return;
		System.arraycopy(this.images,0,images,offset,length);
	}
	
	
	/** Gets the font height. */
	public int getHeight() {
		return isBitmapFont()? size : javax.microedition.lcdui.Font.getFont(face,style,size).getHeight();
	}

	/** Gets the width for the given string. */
	public int getWidth(String s) {
		return isBitmapFont()? _getWidth(s) : javax.microedition.lcdui.Font.getFont(face,style,size).stringWidth(s);
	}
	
	/**
	 * Writes the string into the given graphics object.<br>
	 * It uses the width and height parameters to fit the string according to the anchor.<br>
	 * The anchor value must be a <a href="./Component.html#align">Component ALIGN constant</a>.
	 * This method uses {@link #write(Graphics, String, int, int, int)}.
	 */
	final public void write(Graphics g, String s, int x, int y, int width, int height, int anchor) {
		if((anchor & Graphics.HCENTER) != 0) x += width/2;
		else if((anchor & Graphics.RIGHT) != 0) x += width;
		
		if((anchor & Graphics.VCENTER) != 0) {
			this.write(g, s, x, y + (height-this.getHeight())/2, Graphics.TOP | (anchor & 13));
			return;
		}
		else if((anchor & Graphics.BOTTOM) != 0) {
			this.write(g, s, x, y + height-this.getHeight(), Graphics.TOP | (anchor & 13));
			return;
		}
		this.write(g,s,x,y,anchor);
	}
	
//	/**
//	 * Same as write(g,s,x,y,0,0,0).
//	 * @param g
//	 * @param s
//	 * @param x
//	 * @param y
//	 * @since 1.2
//	 */
//	final public void write(Graphics g, String s, int x, int y) { write(g,s,x,y,0,0,0); }
	
	/**
	 * Writes the string into the given graphics object.
	 * The anchor value must be a <a href="./Component.html#align">Component ALIGN constant</a>.
	 */
	protected void write(Graphics g, String s, int x, int y, int anchor) {
		if(isBitmapFont()) _write(g,s,x,y,anchor);
		else {
			g.setFont(javax.microedition.lcdui.Font.getFont(face,style,size));
			g.setColor(color);
			g.drawString(s,x,y,anchor);
		}
	}
	
	
	
	// ===============
	// class MicroFont
	// ===============
	
	// Returns the index in the charset/image array for the given char
	final private int _getIndex(char c) {
	    int high = charset.length, low = -1, probe;
	    while(high - low > 1) {
	        probe = (high + low) / 2;
	        if(charset[probe] < c) low = probe;
	        else high = probe;
	    }
	    if(!(high == charset.length || charset[high] != c)) return high;
		return -1;
	}
	
	// This private constructor is the MicroFont's format reader
	private Font(InputStream is, char[] charset, int size, int color) throws IOException {
		this.color = color;
		DataInputStream dis = new DataInputStream(is);
		
		// the MicroFont format (not implemented yet)
		dis.readByte();
		
		// read the entire charset
		final int charsetLength = dis.readShort();
		final char[] fullCharset = new char[charsetLength];
		for(int i=0; i<fullCharset.length ;i++)
			fullCharset[i] = dis.readChar();
		
		// the original max height
		final int maxh =  dis.readByte();
		
		// set the charset, either the specified or the entire one
		if(charset != null) this.charset = charset;
		else {
			this.charset = new char[fullCharset.length];
			System.arraycopy(fullCharset,0,this.charset,0,fullCharset.length);
		}
		
		this.images = new Image[this.charset.length];
		
		// sort the charset (a binary search will be used later)
		for(int i=0; i<this.charset.length ;i++)
			for(int j=i+1; j<this.charset.length ;j++)
				if(this.charset[j] < this.charset[i]) {
					final char t = this.charset[i];
					this.charset[i] = this.charset[j];
					this.charset[j] = t;
				}
		
		// load each char's image
		for(int i=0; i<charsetLength ;i++) {
			// the original image's width
			final int width = dis.readByte();
			
			// if there's an specific charset, check if this char is required
			// otherwise, skip its data
			if(charset != null) {
				boolean skip = true;
				for(int c=0; c<charset.length ;c++)
					if(charset[c] == fullCharset[i]) { skip = false; break; }
				if(skip) { dis.skipBytes(width*maxh); continue; }
			}
			
			byte[] raw = new byte[width*maxh];			
			dis.readFully(raw);
			
			// if there's a resizing...
			if(size != 0 && size != maxh) {
				final int newWidth = (size * width) / maxh;
				raw = _resize(raw,maxh,size);
				this.images[_getIndex(fullCharset[i])] = _createRaw(newWidth,size,raw,color);
			}
			else this.images[_getIndex(fullCharset[i])] = _createRaw(width,maxh,raw,color);
		}
		dis.close();
		
		final int correct = 0; // not implemented yet
		this.size = ((size != 0)? size : maxh) + correct;
		spaceWidth = this.size/2;
		grey_pal = null; // free the array memory (setted durning _createRaw)
	}
	
	final private int _getWidth(char c) {
		if(c == ' ') return spaceWidth;
		switch(type) {
		case TYPE_MAPPED: return images[_getIndex(c)].getWidth();
		case TYPE_STRIP: return widths[_getIndex(c)];
		default:
			return 0; // TODO throw exception
		}
	}
	
	final private int _getWidth(final String s) {
		if(s.length() == 0) return 0;
		int len = 0;
		for(int i=0; i<s.length() ;i++)
			len += _getWidth(s.charAt(i)) + charSpacing;
		if(s.length() > 0) len -= charSpacing;
		return len;
	}
	
	final private void _write(final Graphics g, final String s, int x, int y, int anchor) {
		int cx=0, cy=0, cw=0, ch=0;
		
		if(type == TYPE_STRIP) {
			cx = g.getClipX();
			cy = g.getClipY();
			cw = g.getClipWidth();
			ch = g.getClipHeight();
		}
		
		if((anchor & Graphics.RIGHT) != 0) {
			for(int i=s.length()-1; i>= 0 ;i--) {
				if(s.charAt(i) == ' ') x -= (spaceWidth + charSpacing);
				else {
					final int index = _getIndex(s.charAt(i));
					if(type == TYPE_MAPPED) {
						final Image image = images[index];
						x -= image.getWidth();
						g.drawImage(image, x, y, 0);
					}
					else {
						x -= widths[index];
				        g.setClip(x, y, widths[index], size);
				        g.drawImage(strip, x-offsets[index], y-0, 0);				        
				        g.setClip(cx, cy, cw, ch);
					}
					x -= charSpacing;
				}
			}
		}
		else if((anchor & Graphics.HCENTER) != 0) {
			final int w = getWidth(s)/2;
			for(int i=0; i<s.length() ;i++) {
				if(s.charAt(i) == ' ') x += spaceWidth + charSpacing;
				else {			
					final int index = _getIndex(s.charAt(i));
					if(type == TYPE_MAPPED) {
						final Image image = images[index];
						g.drawImage(image, x-w, y, 0);
						x += image.getWidth() + charSpacing;
					}
					else {
				        g.setClip(x-w, y, widths[index], size);
				        g.drawImage(strip, x-offsets[index]-w, y-0, 0);
				        x += widths[index] + charSpacing;				        
				        g.setClip(cx, cy, cw, ch);
					}
				}
			}
		}
		else if((anchor & Graphics.LEFT) != 0 || anchor == 0) {
			for(int i=0; i<s.length() ;i++) {
				if(s.charAt(i) == ' ') x += spaceWidth + charSpacing;
				else {					
					final int index = _getIndex(s.charAt(i));
					if(type == TYPE_MAPPED) {
						final Image image = images[index];
						g.drawImage(image, x, y, 0);
						x += image.getWidth() + charSpacing;
					}
					else {
				        g.setClip(x, y, widths[index], size);
				        g.drawImage(strip, x-offsets[index], y-0, 0);
				        x += widths[index] + charSpacing;
				        g.setClip(cx, cy, cw, ch);
					}
				}
			}
		}
		else throw new IllegalArgumentException();
	}
	
	final static private byte[] _resize(byte[] raw, int height, int newHeight) {
		final int width = raw.length/height;
		final int newWidth = (newHeight * width) / height;
		final byte[] out = new byte[newWidth * newHeight];
		for(int y = 0; y < newHeight; y++) {
			final int dy = y * height / newHeight;
			final int nw = newWidth*y;
			final int ow = width*dy;
			for(int x = 0; x < newWidth; x++)
				out[nw + x] = raw[ow + (x*width / newWidth)];
		}
		return out;
	}
	
	// ==============================================
	// Raw image PNG encoder optimized for MicroFonts
	// ==============================================
	static private final int[] crc_table;
	static private int[] grey_pal;
	static private final byte[] trans_pal;
	static {
		crc_table = new int[256];
		for(int i=0; i<256 ;i++) {
			int c = i;
			for(int k=8; --k>= 0;)
				c = ((c & 1) != 0)? 0xedb88320 ^ (c >>> 1) : c >>> 1; 
			crc_table[i] = c;
		}
		
		trans_pal = new byte[256];
		for(int i=0; i<256; i++)
			trans_pal[i] = (byte) ((i % 16) * 16);
	}
	
	final static private Image _createRaw(int width, int height, final byte[] raw, int color) {
		final int size = 256*3 + 256 + (width+1)*height + 93; // palette * 3 + trans (256) + w*h + chunk stuff
		final byte[] data = new byte[size];
		
		// PNG file signature
		data[0] = -119;
		data[1] = 0x50;
		data[2] = 0x4e;
		data[3] = 0x47;
		data[4] = 0x0d;
		data[5] = 0x0a;
		data[6] = 0x1a;
		data[7] = 0x0a;

		// Header 'IHDR' Chunk
		data[11] = 0x0d;			// Chunk length: 4b (13 bytes)
		_wint(0x49484452, data, 12); // Chunk Name 'IHDR'
		data[19] = (byte) width;	// Width: 4b
		data[23] = (byte) height;	// Height: 4b
		data[24] = 0x08; 			// Bitdepth
		data[25] = 0x03; 			// Color Type (RGB + A)
//		data[26] = 0x00; 			// Compression method
//		data[27] = 0x00; 			// Filter method
//		data[28] = 0x00; 			// Interlace method
		_wint(_crc(data, 12, 17), data, 29);
				
		// Palette 'PLTE' Chunk
		_wint(256 * 3, data, 33); // Chunk length
		_wint(0x504c5445, data, 37); // Chunk Name 'PLTE'
		if(grey_pal == null) {
			grey_pal = new int[256];
			for(int i=0; i<256; i++)
				grey_pal[i] = ((i / 17) << 4);
		}
		for(int i=0, c=41; i<256; i++) {
			final int grey = grey_pal[i];
			data[c++] = (byte) ((grey * ((color & 0x00FF0000) >> 16)) >> 8);
			data[c++] = (byte) ((grey * ((color & 0x0000FF00) >> 8)) >> 8);
			data[c++] = (byte) ((grey * ((color & 0x000000FF) >> 0)) >> 8);
		}
		_wint(_crc(data, 37, 809 - 37), data, 809);

		// Transparency 'tRNS' Chunk
		_wint(256 + 1, data, 813);	 // Chunk length
		_wint(0x74524e53, data, 817); // 'tRNS' Header
		System.arraycopy(trans_pal,0,data,821,256);
		//data[1077] = 0; ??
		_wint(_crc(data, 817, 1078 - 817), data, 1078);

		// Image Data 'IDAT' Chunk
		int compsize = (width + 1) * height;
		_wint(compsize + 11, data, 1082);	// Chunk Length
		_wint(0x49444154, data, 1086);		// Chunk Name 'IDAT'
		data[1090] = (byte) 0x78;			// PNG compression flags
		data[1091] = (byte) 0xda;			// PNG compression flags
		data[1092] = (byte) 0x01;			// PNG final block / No compression
		data[1093] = (byte) (compsize & 0xff);
		data[1094] = (byte) ((compsize >>> 8) & 0xff);
		data[1095] = (byte) (~data[1095 - 2]);
		data[1096] = (byte) (~data[1096 - 2]);
		
		int p = 1097;
		for(int y=0, i=0; y<height; y++) {	// Data copy
			p++;
			System.arraycopy(raw, i, data, p, width);
			p+=width;
			i+=width;
		}
		
		int adler1 = 1;
		int adler2 = 0;
		for (int i = 0; i < compsize; i++) {
			adler1 = adler1 + ((int) data[1097+ i] & 0xff);
			adler2 = adler1 + adler2;
			adler1 %= 65521;
			adler2 %= 65521;
		}
		_wint((adler2 << 16) | adler1, data, p);
		p += 4;
		_wint(_crc(data, 1086, p - 1086), data, p);
		p += 4;

		// Footer 'IEND' Chunk
		p += 4; // Four 0 bytes
		data[p++] = (byte) 'I';
		data[p++] = (byte) 'E';
		data[p++] = (byte) 'N';
		data[p++] = (byte) 'D';
		data[p++] = -82;
		data[p++] = 0x42;
		data[p++] = 0x60;
		data[p++] = -126;

		return Image.createImage(data, 0, p);
	}

	final static private void _wint(long crc, byte[] d, int p) {
		d[p+0] = (byte) ((crc >>> 24) & 255);
		d[p+1] = (byte) ((crc >>> 16) & 255);
		d[p+2] = (byte) ((crc >>> 8) & 255);
		d[p+3] = (byte) (crc & 255);
	}

	final static private long _crc(byte[] buf, int off, int len) {
		int c = ~0;
		while(--len >= 0)
			c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8);
		return (long) ~c & 0xffffffffL;
	}
}
